/*! @azure/msal-common v13.3.1 2023-10-27 */
'use strict';
import { __awaiter, __generator, __assign } from '../_virtual/_tslib.js';
import { AuthorityType } from './AuthorityType.js';
import { isOpenIdConfigResponse } from './OpenIdConfigResponse.js';
import { UrlString } from '../url/UrlString.js';
import { ClientAuthError } from '../error/ClientAuthError.js';
import { Constants, AADAuthorityConstants, AuthorityMetadataSource, RegionDiscoveryOutcomes } from '../utils/Constants.js';
import { EndpointMetadata, InstanceDiscoveryMetadata } from './AuthorityMetadata.js';
import { ClientConfigurationError } from '../error/ClientConfigurationError.js';
import { ProtocolMode } from './ProtocolMode.js';
import { AuthorityMetadataEntity } from '../cache/entities/AuthorityMetadataEntity.js';
import { AzureCloudInstance } from './AuthorityOptions.js';
import { isCloudInstanceDiscoveryResponse } from './CloudInstanceDiscoveryResponse.js';
import { isCloudInstanceDiscoveryErrorResponse } from './CloudInstanceDiscoveryErrorResponse.js';
import { RegionDiscovery } from './RegionDiscovery.js';
import { AuthError } from '../error/AuthError.js';
import { PerformanceEvents } from '../telemetry/performance/PerformanceEvent.js';

/*
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License.
 */
/**
 * The authority class validates the authority URIs used by the user, and retrieves the OpenID Configuration Data from the
 * endpoint. It will store the pertinent config data in this object for use during token calls.
 */
var Authority = /** @class */ (function () {
    function Authority(authority, networkInterface, cacheManager, authorityOptions, logger, performanceClient, correlationId) {
        this.canonicalAuthority = authority;
        this._canonicalAuthority.validateAsUri();
        this.networkInterface = networkInterface;
        this.cacheManager = cacheManager;
        this.authorityOptions = authorityOptions;
        this.regionDiscoveryMetadata = { region_used: undefined, region_source: undefined, region_outcome: undefined };
        this.logger = logger;
        this.performanceClient = performanceClient;
        this.correlationId = correlationId;
        this.regionDiscovery = new RegionDiscovery(networkInterface, this.performanceClient, this.correlationId);
    }
    /**
     * Get {@link AuthorityType}
     * @param authorityUri {@link IUri}
     * @private
     */
    Authority.prototype.getAuthorityType = function (authorityUri) {
        // CIAM auth url pattern is being standardized as: <tenant>.ciamlogin.com
        if (authorityUri.HostNameAndPort.endsWith(Constants.CIAM_AUTH_URL)) {
            return AuthorityType.Ciam;
        }
        var pathSegments = authorityUri.PathSegments;
        if (pathSegments.length) {
            switch (pathSegments[0].toLowerCase()) {
                case Constants.ADFS:
                    return AuthorityType.Adfs;
                case Constants.DSTS:
                    return AuthorityType.Dsts;
            }
        }
        return AuthorityType.Default;
    };
    Object.defineProperty(Authority.prototype, "authorityType", {
        // See above for AuthorityType
        get: function () {
            return this.getAuthorityType(this.canonicalAuthorityUrlComponents);
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(Authority.prototype, "protocolMode", {
        /**
         * ProtocolMode enum representing the way endpoints are constructed.
         */
        get: function () {
            return this.authorityOptions.protocolMode;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(Authority.prototype, "options", {
        /**
         * Returns authorityOptions which can be used to reinstantiate a new authority instance
         */
        get: function () {
            return this.authorityOptions;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(Authority.prototype, "canonicalAuthority", {
        /**
         * A URL that is the authority set by the developer
         */
        get: function () {
            return this._canonicalAuthority.urlString;
        },
        /**
         * Sets canonical authority.
         */
        set: function (url) {
            this._canonicalAuthority = new UrlString(url);
            this._canonicalAuthority.validateAsUri();
            this._canonicalAuthorityUrlComponents = null;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(Authority.prototype, "canonicalAuthorityUrlComponents", {
        /**
         * Get authority components.
         */
        get: function () {
            if (!this._canonicalAuthorityUrlComponents) {
                this._canonicalAuthorityUrlComponents = this._canonicalAuthority.getUrlComponents();
            }
            return this._canonicalAuthorityUrlComponents;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(Authority.prototype, "hostnameAndPort", {
        /**
         * Get hostname and port i.e. login.microsoftonline.com
         */
        get: function () {
            return this.canonicalAuthorityUrlComponents.HostNameAndPort.toLowerCase();
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(Authority.prototype, "tenant", {
        /**
         * Get tenant for authority.
         */
        get: function () {
            return this.canonicalAuthorityUrlComponents.PathSegments[0];
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(Authority.prototype, "authorizationEndpoint", {
        /**
         * OAuth /authorize endpoint for requests
         */
        get: function () {
            if (this.discoveryComplete()) {
                return this.replacePath(this.metadata.authorization_endpoint);
            }
            else {
                throw ClientAuthError.createEndpointDiscoveryIncompleteError("Discovery incomplete.");
            }
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(Authority.prototype, "tokenEndpoint", {
        /**
         * OAuth /token endpoint for requests
         */
        get: function () {
            if (this.discoveryComplete()) {
                return this.replacePath(this.metadata.token_endpoint);
            }
            else {
                throw ClientAuthError.createEndpointDiscoveryIncompleteError("Discovery incomplete.");
            }
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(Authority.prototype, "deviceCodeEndpoint", {
        get: function () {
            if (this.discoveryComplete()) {
                return this.replacePath(this.metadata.token_endpoint.replace("/token", "/devicecode"));
            }
            else {
                throw ClientAuthError.createEndpointDiscoveryIncompleteError("Discovery incomplete.");
            }
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(Authority.prototype, "endSessionEndpoint", {
        /**
         * OAuth logout endpoint for requests
         */
        get: function () {
            if (this.discoveryComplete()) {
                // ROPC policies may not have end_session_endpoint set
                if (!this.metadata.end_session_endpoint) {
                    throw ClientAuthError.createLogoutNotSupportedError();
                }
                return this.replacePath(this.metadata.end_session_endpoint);
            }
            else {
                throw ClientAuthError.createEndpointDiscoveryIncompleteError("Discovery incomplete.");
            }
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(Authority.prototype, "selfSignedJwtAudience", {
        /**
         * OAuth issuer for requests
         */
        get: function () {
            if (this.discoveryComplete()) {
                return this.replacePath(this.metadata.issuer);
            }
            else {
                throw ClientAuthError.createEndpointDiscoveryIncompleteError("Discovery incomplete.");
            }
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(Authority.prototype, "jwksUri", {
        /**
         * Jwks_uri for token signing keys
         */
        get: function () {
            if (this.discoveryComplete()) {
                return this.replacePath(this.metadata.jwks_uri);
            }
            else {
                throw ClientAuthError.createEndpointDiscoveryIncompleteError("Discovery incomplete.");
            }
        },
        enumerable: false,
        configurable: true
    });
    /**
     * Returns a flag indicating that tenant name can be replaced in authority {@link IUri}
     * @param authorityUri {@link IUri}
     * @private
     */
    Authority.prototype.canReplaceTenant = function (authorityUri) {
        return authorityUri.PathSegments.length === 1
            && !Authority.reservedTenantDomains.has(authorityUri.PathSegments[0])
            && this.getAuthorityType(authorityUri) === AuthorityType.Default
            && this.protocolMode === ProtocolMode.AAD;
    };
    /**
     * Replaces tenant in url path with current tenant. Defaults to common.
     * @param urlString
     */
    Authority.prototype.replaceTenant = function (urlString) {
        return urlString.replace(/{tenant}|{tenantid}/g, this.tenant);
    };
    /**
     * Replaces path such as tenant or policy with the current tenant or policy.
     * @param urlString
     */
    Authority.prototype.replacePath = function (urlString) {
        var _this = this;
        var endpoint = urlString;
        var cachedAuthorityUrl = new UrlString(this.metadata.canonical_authority);
        var cachedAuthorityUrlComponents = cachedAuthorityUrl.getUrlComponents();
        var cachedAuthorityParts = cachedAuthorityUrlComponents.PathSegments;
        var currentAuthorityParts = this.canonicalAuthorityUrlComponents.PathSegments;
        currentAuthorityParts.forEach(function (currentPart, index) {
            var cachedPart = cachedAuthorityParts[index];
            if (index === 0 && _this.canReplaceTenant(cachedAuthorityUrlComponents)) {
                var tenantId = (new UrlString(_this.metadata.authorization_endpoint)).getUrlComponents().PathSegments[0];
                /**
                 * Check if AAD canonical authority contains tenant domain name, for example "testdomain.onmicrosoft.com",
                 * by comparing its first path segment to the corresponding authorization endpoint path segment, which is
                 * always resolved with tenant id by OIDC.
                 */
                if (cachedPart !== tenantId) {
                    _this.logger.verbose("Replacing tenant domain name " + cachedPart + " with id " + tenantId);
                    cachedPart = tenantId;
                }
            }
            if (currentPart !== cachedPart) {
                endpoint = endpoint.replace("/" + cachedPart + "/", "/" + currentPart + "/");
            }
        });
        return this.replaceTenant(endpoint);
    };
    Object.defineProperty(Authority.prototype, "defaultOpenIdConfigurationEndpoint", {
        /**
         * The default open id configuration endpoint for any canonical authority.
         */
        get: function () {
            if (this.authorityType === AuthorityType.Adfs ||
                this.authorityType === AuthorityType.Dsts ||
                this.protocolMode === ProtocolMode.OIDC) {
                return this.canonicalAuthority + ".well-known/openid-configuration";
            }
            return this.canonicalAuthority + "v2.0/.well-known/openid-configuration";
        },
        enumerable: false,
        configurable: true
    });
    /**
     * Boolean that returns whethr or not tenant discovery has been completed.
     */
    Authority.prototype.discoveryComplete = function () {
        return !!this.metadata;
    };
    /**
     * Perform endpoint discovery to discover aliases, preferred_cache, preferred_network
     * and the /authorize, /token and logout endpoints.
     */
    Authority.prototype.resolveEndpointsAsync = function () {
        var _a, _b, _c;
        return __awaiter(this, void 0, void 0, function () {
            var metadataEntity, cloudDiscoverySource, endpointSource, cacheKey;
            return __generator(this, function (_d) {
                switch (_d.label) {
                    case 0:
                        (_a = this.performanceClient) === null || _a === void 0 ? void 0 : _a.addQueueMeasurement(PerformanceEvents.AuthorityResolveEndpointsAsync, this.correlationId);
                        metadataEntity = this.cacheManager.getAuthorityMetadataByAlias(this.hostnameAndPort);
                        if (!metadataEntity) {
                            metadataEntity = new AuthorityMetadataEntity();
                            metadataEntity.updateCanonicalAuthority(this.canonicalAuthority);
                        }
                        (_b = this.performanceClient) === null || _b === void 0 ? void 0 : _b.setPreQueueTime(PerformanceEvents.AuthorityUpdateCloudDiscoveryMetadata, this.correlationId);
                        return [4 /*yield*/, this.updateCloudDiscoveryMetadata(metadataEntity)];
                    case 1:
                        cloudDiscoverySource = _d.sent();
                        this.canonicalAuthority = this.canonicalAuthority.replace(this.hostnameAndPort, metadataEntity.preferred_network);
                        (_c = this.performanceClient) === null || _c === void 0 ? void 0 : _c.setPreQueueTime(PerformanceEvents.AuthorityUpdateEndpointMetadata, this.correlationId);
                        return [4 /*yield*/, this.updateEndpointMetadata(metadataEntity)];
                    case 2:
                        endpointSource = _d.sent();
                        if (cloudDiscoverySource !== AuthorityMetadataSource.CACHE && endpointSource !== AuthorityMetadataSource.CACHE) {
                            // Reset the expiration time unless both values came from a successful cache lookup
                            metadataEntity.resetExpiresAt();
                            metadataEntity.updateCanonicalAuthority(this.canonicalAuthority);
                        }
                        cacheKey = this.cacheManager.generateAuthorityMetadataCacheKey(metadataEntity.preferred_cache);
                        this.cacheManager.setAuthorityMetadata(cacheKey, metadataEntity);
                        this.metadata = metadataEntity;
                        return [2 /*return*/];
                }
            });
        });
    };
    /**
     * Update AuthorityMetadataEntity with new endpoints and return where the information came from
     * @param metadataEntity
     */
    Authority.prototype.updateEndpointMetadata = function (metadataEntity) {
        var _a, _b, _c, _d, _e, _f;
        return __awaiter(this, void 0, void 0, function () {
            var metadata, harcodedMetadata;
            return __generator(this, function (_g) {
                switch (_g.label) {
                    case 0:
                        (_a = this.performanceClient) === null || _a === void 0 ? void 0 : _a.addQueueMeasurement(PerformanceEvents.AuthorityUpdateEndpointMetadata, this.correlationId);
                        metadata = this.getEndpointMetadataFromConfig();
                        if (metadata) {
                            metadataEntity.updateEndpointMetadata(metadata, false);
                            return [2 /*return*/, AuthorityMetadataSource.CONFIG];
                        }
                        if (this.isAuthoritySameType(metadataEntity) && metadataEntity.endpointsFromNetwork && !metadataEntity.isExpired()) {
                            // No need to update
                            return [2 /*return*/, AuthorityMetadataSource.CACHE];
                        }
                        (_b = this.performanceClient) === null || _b === void 0 ? void 0 : _b.setPreQueueTime(PerformanceEvents.AuthorityGetEndpointMetadataFromNetwork, this.correlationId);
                        return [4 /*yield*/, this.getEndpointMetadataFromNetwork()];
                    case 1:
                        metadata = _g.sent();
                        if (!metadata) return [3 /*break*/, 4];
                        if (!((_c = this.authorityOptions.azureRegionConfiguration) === null || _c === void 0 ? void 0 : _c.azureRegion)) return [3 /*break*/, 3];
                        (_d = this.performanceClient) === null || _d === void 0 ? void 0 : _d.setPreQueueTime(PerformanceEvents.AuthorityUpdateMetadataWithRegionalInformation, this.correlationId);
                        return [4 /*yield*/, this.updateMetadataWithRegionalInformation(metadata)];
                    case 2:
                        metadata = _g.sent();
                        _g.label = 3;
                    case 3:
                        metadataEntity.updateEndpointMetadata(metadata, true);
                        return [2 /*return*/, AuthorityMetadataSource.NETWORK];
                    case 4:
                        harcodedMetadata = this.getEndpointMetadataFromHardcodedValues();
                        if (!(harcodedMetadata && !this.authorityOptions.skipAuthorityMetadataCache)) return [3 /*break*/, 7];
                        if (!((_e = this.authorityOptions.azureRegionConfiguration) === null || _e === void 0 ? void 0 : _e.azureRegion)) return [3 /*break*/, 6];
                        (_f = this.performanceClient) === null || _f === void 0 ? void 0 : _f.setPreQueueTime(PerformanceEvents.AuthorityUpdateMetadataWithRegionalInformation, this.correlationId);
                        return [4 /*yield*/, this.updateMetadataWithRegionalInformation(harcodedMetadata)];
                    case 5:
                        harcodedMetadata = _g.sent();
                        _g.label = 6;
                    case 6:
                        metadataEntity.updateEndpointMetadata(harcodedMetadata, false);
                        return [2 /*return*/, AuthorityMetadataSource.HARDCODED_VALUES];
                    case 7: throw ClientAuthError.createUnableToGetOpenidConfigError(this.defaultOpenIdConfigurationEndpoint);
                }
            });
        });
    };
    /**
     * Compares the number of url components after the domain to determine if the cached
     * authority metadata can be used for the requested authority. Protects against same domain different
     * authority such as login.microsoftonline.com/tenant and login.microsoftonline.com/tfp/tenant/policy
     * @param metadataEntity
     */
    Authority.prototype.isAuthoritySameType = function (metadataEntity) {
        var cachedAuthorityUrl = new UrlString(metadataEntity.canonical_authority);
        var cachedParts = cachedAuthorityUrl.getUrlComponents().PathSegments;
        return cachedParts.length === this.canonicalAuthorityUrlComponents.PathSegments.length;
    };
    /**
     * Parse authorityMetadata config option
     */
    Authority.prototype.getEndpointMetadataFromConfig = function () {
        if (this.authorityOptions.authorityMetadata) {
            try {
                return JSON.parse(this.authorityOptions.authorityMetadata);
            }
            catch (e) {
                throw ClientConfigurationError.createInvalidAuthorityMetadataError();
            }
        }
        return null;
    };
    /**
     * Gets OAuth endpoints from the given OpenID configuration endpoint.
     *
     * @param hasHardcodedMetadata boolean
     */
    Authority.prototype.getEndpointMetadataFromNetwork = function () {
        var _a;
        return __awaiter(this, void 0, void 0, function () {
            var options, response;
            return __generator(this, function (_b) {
                switch (_b.label) {
                    case 0:
                        (_a = this.performanceClient) === null || _a === void 0 ? void 0 : _a.addQueueMeasurement(PerformanceEvents.AuthorityGetEndpointMetadataFromNetwork, this.correlationId);
                        options = {};
                        _b.label = 1;
                    case 1:
                        _b.trys.push([1, 3, , 4]);
                        return [4 /*yield*/, this.networkInterface.
                                sendGetRequestAsync(this.defaultOpenIdConfigurationEndpoint, options)];
                    case 2:
                        response = _b.sent();
                        return [2 /*return*/, isOpenIdConfigResponse(response.body) ? response.body : null];
                    case 3:
                        _b.sent();
                        return [2 /*return*/, null];
                    case 4: return [2 /*return*/];
                }
            });
        });
    };
    /**
     * Get OAuth endpoints for common authorities.
     */
    Authority.prototype.getEndpointMetadataFromHardcodedValues = function () {
        if (this.canonicalAuthority in EndpointMetadata) {
            return EndpointMetadata[this.canonicalAuthority];
        }
        return null;
    };
    /**
     * Update the retrieved metadata with regional information.
     * User selected Azure region will be used if configured.
     */
    Authority.prototype.updateMetadataWithRegionalInformation = function (metadata) {
        var _a, _b, _c, _d;
        return __awaiter(this, void 0, void 0, function () {
            var userConfiguredAzureRegion, autodetectedRegionName;
            return __generator(this, function (_e) {
                switch (_e.label) {
                    case 0:
                        (_a = this.performanceClient) === null || _a === void 0 ? void 0 : _a.addQueueMeasurement(PerformanceEvents.AuthorityUpdateMetadataWithRegionalInformation, this.correlationId);
                        userConfiguredAzureRegion = (_b = this.authorityOptions.azureRegionConfiguration) === null || _b === void 0 ? void 0 : _b.azureRegion;
                        if (!userConfiguredAzureRegion) return [3 /*break*/, 2];
                        if (userConfiguredAzureRegion !== Constants.AZURE_REGION_AUTO_DISCOVER_FLAG) {
                            this.regionDiscoveryMetadata.region_outcome = RegionDiscoveryOutcomes.CONFIGURED_NO_AUTO_DETECTION;
                            this.regionDiscoveryMetadata.region_used = userConfiguredAzureRegion;
                            return [2 /*return*/, Authority.replaceWithRegionalInformation(metadata, userConfiguredAzureRegion)];
                        }
                        (_c = this.performanceClient) === null || _c === void 0 ? void 0 : _c.setPreQueueTime(PerformanceEvents.RegionDiscoveryDetectRegion, this.correlationId);
                        return [4 /*yield*/, this.regionDiscovery.detectRegion((_d = this.authorityOptions.azureRegionConfiguration) === null || _d === void 0 ? void 0 : _d.environmentRegion, this.regionDiscoveryMetadata)];
                    case 1:
                        autodetectedRegionName = _e.sent();
                        if (autodetectedRegionName) {
                            this.regionDiscoveryMetadata.region_outcome = RegionDiscoveryOutcomes.AUTO_DETECTION_REQUESTED_SUCCESSFUL;
                            this.regionDiscoveryMetadata.region_used = autodetectedRegionName;
                            return [2 /*return*/, Authority.replaceWithRegionalInformation(metadata, autodetectedRegionName)];
                        }
                        this.regionDiscoveryMetadata.region_outcome = RegionDiscoveryOutcomes.AUTO_DETECTION_REQUESTED_FAILED;
                        _e.label = 2;
                    case 2: return [2 /*return*/, metadata];
                }
            });
        });
    };
    /**
     * Updates the AuthorityMetadataEntity with new aliases, preferred_network and preferred_cache
     * and returns where the information was retrieved from
     * @param metadataEntity
     * @returns AuthorityMetadataSource
     */
    Authority.prototype.updateCloudDiscoveryMetadata = function (metadataEntity) {
        var _a, _b;
        return __awaiter(this, void 0, void 0, function () {
            var metadata, metadataEntityExpired, harcodedMetadata;
            return __generator(this, function (_c) {
                switch (_c.label) {
                    case 0:
                        (_a = this.performanceClient) === null || _a === void 0 ? void 0 : _a.addQueueMeasurement(PerformanceEvents.AuthorityUpdateCloudDiscoveryMetadata, this.correlationId);
                        // attempt to read metadata from the config
                        this.logger.verbose("Attempting to get cloud discovery metadata in the config");
                        this.logger.verbosePii("Known Authorities: " + (this.authorityOptions.knownAuthorities || Constants.NOT_APPLICABLE));
                        this.logger.verbosePii("Authority Metadata: " + (this.authorityOptions.authorityMetadata || Constants.NOT_APPLICABLE));
                        this.logger.verbosePii("Canonical Authority: " + (metadataEntity.canonical_authority || Constants.NOT_APPLICABLE));
                        metadata = this.getCloudDiscoveryMetadataFromConfig();
                        if (metadata) {
                            this.logger.verbose("Found cloud discovery metadata in the config.");
                            metadataEntity.updateCloudDiscoveryMetadata(metadata, false);
                            return [2 /*return*/, AuthorityMetadataSource.CONFIG];
                        }
                        // If the cached metadata came from config but that config was not passed to this instance, we must go to the network
                        this.logger.verbose("Did not find cloud discovery metadata in the config... Attempting to get cloud discovery metadata from the cache.");
                        metadataEntityExpired = metadataEntity.isExpired();
                        if (this.isAuthoritySameType(metadataEntity) && metadataEntity.aliasesFromNetwork && !metadataEntityExpired) {
                            this.logger.verbose("Found metadata in the cache.");
                            // No need to update
                            return [2 /*return*/, AuthorityMetadataSource.CACHE];
                        }
                        else if (metadataEntityExpired) {
                            this.logger.verbose("The metadata entity is expired.");
                        }
                        this.logger.verbose("Did not find cloud discovery metadata in the cache... Attempting to get cloud discovery metadata from the network.");
                        (_b = this.performanceClient) === null || _b === void 0 ? void 0 : _b.setPreQueueTime(PerformanceEvents.AuthorityGetCloudDiscoveryMetadataFromNetwork, this.correlationId);
                        return [4 /*yield*/, this.getCloudDiscoveryMetadataFromNetwork()];
                    case 1:
                        metadata = _c.sent();
                        if (metadata) {
                            this.logger.verbose("cloud discovery metadata was successfully returned from getCloudDiscoveryMetadataFromNetwork()");
                            metadataEntity.updateCloudDiscoveryMetadata(metadata, true);
                            return [2 /*return*/, AuthorityMetadataSource.NETWORK];
                        }
                        this.logger.verbose("Did not find cloud discovery metadata from the network... Attempting to get cloud discovery metadata from hardcoded values.");
                        harcodedMetadata = this.getCloudDiscoveryMetadataFromHarcodedValues();
                        if (harcodedMetadata && !this.options.skipAuthorityMetadataCache) {
                            this.logger.verbose("Found cloud discovery metadata from hardcoded values.");
                            metadataEntity.updateCloudDiscoveryMetadata(harcodedMetadata, false);
                            return [2 /*return*/, AuthorityMetadataSource.HARDCODED_VALUES];
                        }
                        // Metadata could not be obtained from the config, cache, network or hardcoded values
                        this.logger.error("Did not find cloud discovery metadata from hardcoded values... Metadata could not be obtained from config, cache, network or hardcoded values. Throwing Untrusted Authority Error.");
                        throw ClientConfigurationError.createUntrustedAuthorityError();
                }
            });
        });
    };
    /**
     * Parse cloudDiscoveryMetadata config or check knownAuthorities
     */
    Authority.prototype.getCloudDiscoveryMetadataFromConfig = function () {
        // CIAM does not support cloud discovery metadata
        if (this.authorityType === AuthorityType.Ciam) {
            this.logger.verbose("CIAM authorities do not support cloud discovery metadata, generate the aliases from authority host.");
            return Authority.createCloudDiscoveryMetadataFromHost(this.hostnameAndPort);
        }
        // Check if network response was provided in config
        if (this.authorityOptions.cloudDiscoveryMetadata) {
            this.logger.verbose("The cloud discovery metadata has been provided as a network response, in the config.");
            try {
                this.logger.verbose("Attempting to parse the cloud discovery metadata.");
                var parsedResponse = JSON.parse(this.authorityOptions.cloudDiscoveryMetadata);
                var metadata = Authority.getCloudDiscoveryMetadataFromNetworkResponse(parsedResponse.metadata, this.hostnameAndPort);
                this.logger.verbose("Parsed the cloud discovery metadata.");
                if (metadata) {
                    this.logger.verbose("There is returnable metadata attached to the parsed cloud discovery metadata.");
                    return metadata;
                }
                else {
                    this.logger.verbose("There is no metadata attached to the parsed cloud discovery metadata.");
                }
            }
            catch (e) {
                this.logger.verbose("Unable to parse the cloud discovery metadata. Throwing Invalid Cloud Discovery Metadata Error.");
                throw ClientConfigurationError.createInvalidCloudDiscoveryMetadataError();
            }
        }
        // If cloudDiscoveryMetadata is empty or does not contain the host, check knownAuthorities
        if (this.isInKnownAuthorities()) {
            this.logger.verbose("The host is included in knownAuthorities. Creating new cloud discovery metadata from the host.");
            return Authority.createCloudDiscoveryMetadataFromHost(this.hostnameAndPort);
        }
        return null;
    };
    /**
     * Called to get metadata from network if CloudDiscoveryMetadata was not populated by config
     *
     * @param hasHardcodedMetadata boolean
     */
    Authority.prototype.getCloudDiscoveryMetadataFromNetwork = function () {
        var _a;
        return __awaiter(this, void 0, void 0, function () {
            var instanceDiscoveryEndpoint, options, match, response, typedResponseBody, metadata, error_1, typedError;
            return __generator(this, function (_b) {
                switch (_b.label) {
                    case 0:
                        (_a = this.performanceClient) === null || _a === void 0 ? void 0 : _a.addQueueMeasurement(PerformanceEvents.AuthorityGetCloudDiscoveryMetadataFromNetwork, this.correlationId);
                        instanceDiscoveryEndpoint = "" + Constants.AAD_INSTANCE_DISCOVERY_ENDPT + this.canonicalAuthority + "oauth2/v2.0/authorize";
                        options = {};
                        match = null;
                        _b.label = 1;
                    case 1:
                        _b.trys.push([1, 3, , 4]);
                        return [4 /*yield*/, this.networkInterface.sendGetRequestAsync(instanceDiscoveryEndpoint, options)];
                    case 2:
                        response = _b.sent();
                        typedResponseBody = void 0;
                        metadata = void 0;
                        if (isCloudInstanceDiscoveryResponse(response.body)) {
                            typedResponseBody = response.body;
                            metadata = typedResponseBody.metadata;
                            this.logger.verbosePii("tenant_discovery_endpoint is: " + typedResponseBody.tenant_discovery_endpoint);
                        }
                        else if (isCloudInstanceDiscoveryErrorResponse(response.body)) {
                            this.logger.warning("A CloudInstanceDiscoveryErrorResponse was returned. The cloud instance discovery network request's status code is: " + response.status);
                            typedResponseBody = response.body;
                            if (typedResponseBody.error === Constants.INVALID_INSTANCE) {
                                this.logger.error("The CloudInstanceDiscoveryErrorResponse error is invalid_instance.");
                                return [2 /*return*/, null];
                            }
                            this.logger.warning("The CloudInstanceDiscoveryErrorResponse error is " + typedResponseBody.error);
                            this.logger.warning("The CloudInstanceDiscoveryErrorResponse error description is " + typedResponseBody.error_description);
                            this.logger.warning("Setting the value of the CloudInstanceDiscoveryMetadata (returned from the network) to []");
                            metadata = [];
                        }
                        else {
                            this.logger.error("AAD did not return a CloudInstanceDiscoveryResponse or CloudInstanceDiscoveryErrorResponse");
                            return [2 /*return*/, null];
                        }
                        this.logger.verbose("Attempting to find a match between the developer's authority and the CloudInstanceDiscoveryMetadata returned from the network request.");
                        match = Authority.getCloudDiscoveryMetadataFromNetworkResponse(metadata, this.hostnameAndPort);
                        return [3 /*break*/, 4];
                    case 3:
                        error_1 = _b.sent();
                        if (error_1 instanceof AuthError) {
                            this.logger.error("There was a network error while attempting to get the cloud discovery instance metadata.\nError: " + error_1.errorCode + "\nError Description: " + error_1.errorMessage);
                        }
                        else {
                            typedError = error_1;
                            this.logger.error("A non-MSALJS error was thrown while attempting to get the cloud instance discovery metadata.\nError: " + typedError.name + "\nError Description: " + typedError.message);
                        }
                        return [2 /*return*/, null];
                    case 4:
                        // Custom Domain scenario, host is trusted because Instance Discovery call succeeded
                        if (!match) {
                            this.logger.warning("The developer's authority was not found within the CloudInstanceDiscoveryMetadata returned from the network request.");
                            this.logger.verbose("Creating custom Authority for custom domain scenario.");
                            match = Authority.createCloudDiscoveryMetadataFromHost(this.hostnameAndPort);
                        }
                        return [2 /*return*/, match];
                }
            });
        });
    };
    /**
     * Get cloud discovery metadata for common authorities
     */
    Authority.prototype.getCloudDiscoveryMetadataFromHarcodedValues = function () {
        if (this.canonicalAuthority in InstanceDiscoveryMetadata) {
            return InstanceDiscoveryMetadata[this.canonicalAuthority];
        }
        return null;
    };
    /**
     * Helper function to determine if this host is included in the knownAuthorities config option
     */
    Authority.prototype.isInKnownAuthorities = function () {
        var _this = this;
        var matches = this.authorityOptions.knownAuthorities.filter(function (authority) {
            return UrlString.getDomainFromUrl(authority).toLowerCase() === _this.hostnameAndPort;
        });
        return matches.length > 0;
    };
    /**
     * helper function to populate the authority based on azureCloudOptions
     * @param authorityString
     * @param azureCloudOptions
     */
    Authority.generateAuthority = function (authorityString, azureCloudOptions) {
        var authorityAzureCloudInstance;
        if (azureCloudOptions && azureCloudOptions.azureCloudInstance !== AzureCloudInstance.None) {
            var tenant = azureCloudOptions.tenant ? azureCloudOptions.tenant : Constants.DEFAULT_COMMON_TENANT;
            authorityAzureCloudInstance = azureCloudOptions.azureCloudInstance + "/" + tenant + "/";
        }
        return authorityAzureCloudInstance ? authorityAzureCloudInstance : authorityString;
    };
    /**
     * Creates cloud discovery metadata object from a given host
     * @param host
     */
    Authority.createCloudDiscoveryMetadataFromHost = function (host) {
        return {
            preferred_network: host,
            preferred_cache: host,
            aliases: [host]
        };
    };
    /**
     * Searches instance discovery network response for the entry that contains the host in the aliases list
     * @param response
     * @param authority
     */
    Authority.getCloudDiscoveryMetadataFromNetworkResponse = function (response, authority) {
        for (var i = 0; i < response.length; i++) {
            var metadata = response[i];
            if (metadata.aliases.indexOf(authority) > -1) {
                return metadata;
            }
        }
        return null;
    };
    /**
     * helper function to generate environment from authority object
     */
    Authority.prototype.getPreferredCache = function () {
        if (this.discoveryComplete()) {
            return this.metadata.preferred_cache;
        }
        else {
            throw ClientAuthError.createEndpointDiscoveryIncompleteError("Discovery incomplete.");
        }
    };
    /**
     * Returns whether or not the provided host is an alias of this authority instance
     * @param host
     */
    Authority.prototype.isAlias = function (host) {
        return this.metadata.aliases.indexOf(host) > -1;
    };
    /**
     * Checks whether the provided host is that of a public cloud authority
     *
     * @param authority string
     * @returns bool
     */
    Authority.isPublicCloudAuthority = function (host) {
        return Constants.KNOWN_PUBLIC_CLOUDS.indexOf(host) >= 0;
    };
    /**
     * Rebuild the authority string with the region
     *
     * @param host string
     * @param region string
     */
    Authority.buildRegionalAuthorityString = function (host, region, queryString) {
        // Create and validate a Url string object with the initial authority string
        var authorityUrlInstance = new UrlString(host);
        authorityUrlInstance.validateAsUri();
        var authorityUrlParts = authorityUrlInstance.getUrlComponents();
        var hostNameAndPort = region + "." + authorityUrlParts.HostNameAndPort;
        if (this.isPublicCloudAuthority(authorityUrlParts.HostNameAndPort)) {
            hostNameAndPort = region + "." + Constants.REGIONAL_AUTH_PUBLIC_CLOUD_SUFFIX;
        }
        // Include the query string portion of the url
        var url = UrlString.constructAuthorityUriFromObject(__assign(__assign({}, authorityUrlInstance.getUrlComponents()), { HostNameAndPort: hostNameAndPort })).urlString;
        // Add the query string if a query string was provided
        if (queryString)
            return url + "?" + queryString;
        return url;
    };
    /**
     * Replace the endpoints in the metadata object with their regional equivalents.
     *
     * @param metadata OpenIdConfigResponse
     * @param azureRegion string
     */
    Authority.replaceWithRegionalInformation = function (metadata, azureRegion) {
        metadata.authorization_endpoint = Authority.buildRegionalAuthorityString(metadata.authorization_endpoint, azureRegion);
        // TODO: Enquire on whether we should leave the query string or remove it before releasing the feature
        metadata.token_endpoint = Authority.buildRegionalAuthorityString(metadata.token_endpoint, azureRegion, Constants.REGIONAL_AUTH_NON_MSI_QUERY_STRING);
        if (metadata.end_session_endpoint) {
            metadata.end_session_endpoint = Authority.buildRegionalAuthorityString(metadata.end_session_endpoint, azureRegion);
        }
        return metadata;
    };
    /**
     * Transform CIAM_AUTHORIY as per the below rules:
     * If no path segments found and it is a CIAM authority (hostname ends with .ciamlogin.com), then transform it
     *
     * NOTE: The transformation path should go away once STS supports CIAM with the format: `tenantIdorDomain.ciamlogin.com`
     * `ciamlogin.com` can also change in the future and we should accommodate the same
     *
     * @param authority
     */
    Authority.transformCIAMAuthority = function (authority) {
        var ciamAuthority = authority.endsWith(Constants.FORWARD_SLASH) ? authority : "" + authority + Constants.FORWARD_SLASH;
        var authorityUrl = new UrlString(authority);
        var authorityUrlComponents = authorityUrl.getUrlComponents();
        // check if transformation is needed
        if (authorityUrlComponents.PathSegments.length === 0 && (authorityUrlComponents.HostNameAndPort.endsWith(Constants.CIAM_AUTH_URL))) {
            var tenantIdOrDomain = authorityUrlComponents.HostNameAndPort.split(".")[0];
            ciamAuthority = "" + ciamAuthority + tenantIdOrDomain + Constants.AAD_TENANT_DOMAIN_SUFFIX;
        }
        return ciamAuthority;
    };
    // Reserved tenant domain names that will not be replaced with tenant id
    Authority.reservedTenantDomains = (new Set([
        "{tenant}",
        "{tenantid}",
        AADAuthorityConstants.COMMON,
        AADAuthorityConstants.CONSUMERS,
        AADAuthorityConstants.ORGANIZATIONS
    ]));
    return Authority;
}());

export { Authority };
//# sourceMappingURL=Authority.js.map
